#ifndef __BOUNDED_COUNTER_HPP__
#define __BOUNDED_COUNTER_HPP__

namespace ipc {

typedef struct {
	size_t actual;
	size_t max;
} sBoundedValue;

template <class SEMAPHORE, class SHARED_MEMORY>
class bounded_counter_base {
	protected:
		SEMAPHORE mutex;
		SHARED_MEMORY value;
		bool ultima_operacion;

		//Un counter no puede ser copiado.
		bounded_counter_base (const bounded_counter_base& other);
		bounded_counter_base& operator= (const bounded_counter_base& other);
	public:
		explicit bounded_counter_base (key_t shm_key, key_t sem_key, size_t sem_num) : mutex(sem_key,sem_num), value(shm_key) {
			ultima_operacion = true;
		}

		inline int get (int mode) {
			int error = mutex.get(mode);
			if (error == 0)
				error = value.get(mode);
			return error;
		}

		inline int free (bool auto_destroy = false) {
			return value.free(auto_destroy);
		}

		inline int destroy() {
			int error = mutex.destroy();
			if (error == 0)
				error = value.destroy();
			return error;
		}
	
		inline bool init(size_t init_value, size_t max) {
			bool exito = false;
			mutex.init(1);
			ultima_operacion = (init_value <= max);
			if (init_value <= max) {
				mutex.wait();
					(*value).actual = init_value;
					(*value).max = max;
				mutex.signal();
				exito = true;
			}
			return exito;
		}

		inline size_t setActual (size_t new_value) {
			mutex.wait();
				if ( new_value > (*value).max )
					new_value = (*value).max;
				(*value).actual = new_value;
				ultima_operacion = true;
			mutex.signal();
			return new_value;
		}

		inline size_t inc() {
			size_t anterior = 0;
			mutex.wait();
				anterior = (*value).actual;
				ultima_operacion = (anterior < (*value).max );
				if (ultima_operacion)
					(*value).actual++;
			mutex.signal();
			return anterior;
		}

		inline size_t dec () {
			size_t anterior = 0;
			mutex.wait();
				anterior = (*value).actual;
				ultima_operacion = (anterior > 0);
				if (ultima_operacion)
					(*value).actual--;
			mutex.signal();
			return anterior;
		}

		// Informa si la ultima llamada a inc o dec modifico el valor actual.
		bool isUltimaOperacionOK() const {
			return this->ultima_operacion;
		}

		inline size_t getActual() {
			size_t actual = 0;
			mutex.wait();
				actual = (*value).actual;
			mutex.signal();
			return actual;
		}

}; // class bounded_counter_base

typedef bounded_counter_base<semaphore, shared_memory<sBoundedValue> > bounded_counter;
typedef bounded_counter_base<semaphore_with_exceptions, shared_memory_with_exceptions<sBoundedValue> > bounded_counter_with_exceptions;


} // namespace ipc

#endif /* __BOUNDED_COUNTER_HPP__ */

